﻿using AutoMapper;
using Mes.Infrastructure.Data.BoundedContext.Repositories.Operations;
using Mes.Domain.BoundedContext.Entities.Operations;
using Mes.Application.DTO.Inputs.Operations;
using Mes.Application.DTO.Results.Operations;
using Microsoft.EntityFrameworkCore;
using Mes.Application.Common;
using Mes.Application.Common.Resource;
using Mes.Infrastructure.Data.BoundedContext.Repositories.MasterData;

namespace Mes.Application.Services.Operations
{
    public class OperService : IOperService
    {
        private readonly IOperRepository _operRepository;
        private readonly IOperEntRouteRepository _operEntRouteRepository;
        private readonly IOperEntLinkRepository _operEntLinkRepository;
        private readonly IOperAttrRepository _operAttrRepository;
        private readonly IOperStepGrpRepository _operStepGrpRepository;
        private readonly IOperStepRepository _operStepRepository;
        private readonly IMapper _mapper;
        private readonly IProcessRepository _processRepository;
        private readonly IProcessRouteRepository _processRouteRepository;
        private readonly IStdOperEntRouteRepository _stdOperEntRouteRepository;
        private readonly IStdOperRepository _stdOperRepository;
        private readonly IStdOperEntLinkRepository _stdOperEntLinkRepository;
        private readonly IStdOperAttrRepository _stdOperAttrRepository;
        private readonly IAttrRepository _attrRepository;
        private readonly IProductionUnitRepository _productionUnitRepository;
        private readonly IStdOperStepGrpRepository _stdOperStepGrpRepository;
        private readonly IStdOperStepRepository _stdOperStepRepository;
        private readonly IItemProcessLinkRepository _itemProcessLinkRepository;
        private readonly IMaterialBOMRepository _materialBOMRepository;
        private readonly IMaterialMaterialBOMLinkRepository _materialBOMLinkRepository;
        private readonly IMaterialBOMMaterialLinkRepository _materialLinkRepository;
        private readonly IMaterialRepository _materialRepository;
        private readonly IBomItemOperLinkRepository _bomItemOperLinkRepository;
        private readonly IBomItemOperStepLinkRepository _bomItemOperStepLinkRepository;
        public OperService(IOperRepository operRepository,
            IOperEntRouteRepository operEntRouteRepository,
            IOperEntLinkRepository operEntLinkRepository,
            IOperAttrRepository operAttrRepository,
            IOperStepGrpRepository operStepGrpRepository,
            IOperStepRepository operStepRepository,
            IMapper mapper,
            IProcessRepository processRepository,
            IProcessRouteRepository processRouteRepository,
            IStdOperEntRouteRepository stdOperEntRouteRepository,
            IStdOperRepository stdOperRepository,
            IStdOperEntLinkRepository stdOperEntLinkRepository,
            IStdOperAttrRepository stdOperAttrRepository,
            IAttrRepository attrRepository,
            IProductionUnitRepository productionUnitRepository,
            IStdOperStepGrpRepository stdOperStepGrpRepository,
            IStdOperStepRepository stdOperStepRepository,
            IItemProcessLinkRepository itemProcessLinkRepository,
            IMaterialBOMRepository materialBOMRepository,
            IMaterialMaterialBOMLinkRepository materialBOMLinkRepository,
            IMaterialBOMMaterialLinkRepository materialLinkRepository,
            IMaterialRepository materialRepository,
            IBomItemOperLinkRepository bomItemOperLinkRepository,
            IBomItemOperStepLinkRepository bomItemOperStepLinkRepository)
        {
            if (operRepository == null || operEntRouteRepository == null || operEntLinkRepository == null
                || operAttrRepository == null || operStepGrpRepository == null || operStepRepository == null
                || mapper == null || processRepository == null || processRouteRepository == null
                || stdOperEntRouteRepository == null || stdOperRepository == null || stdOperEntLinkRepository == null
                || stdOperAttrRepository == null || attrRepository == null || productionUnitRepository == null
                || stdOperStepGrpRepository == null || stdOperStepRepository == null || itemProcessLinkRepository == null
                || materialBOMRepository == null || materialBOMLinkRepository == null || materialLinkRepository == null
                || materialRepository == null || bomItemOperLinkRepository == null || bomItemOperStepLinkRepository == null)
            {
                throw new ArgumentNullException();
            }
            _operRepository = operRepository; //注入仓储类
            _operEntRouteRepository = operEntRouteRepository;
            _operEntLinkRepository = operEntLinkRepository;
            _operAttrRepository = operAttrRepository;
            _operStepGrpRepository = operStepGrpRepository;
            _operStepRepository = operStepRepository;
            _mapper = mapper;
            _processRepository = processRepository;
            _processRouteRepository = processRouteRepository;
            _stdOperEntRouteRepository = stdOperEntRouteRepository;
            _stdOperRepository = stdOperRepository;
            _stdOperEntLinkRepository = stdOperEntLinkRepository;
            _stdOperAttrRepository = stdOperAttrRepository;
            _attrRepository = attrRepository;
            _productionUnitRepository = productionUnitRepository;
            _stdOperStepGrpRepository = stdOperStepGrpRepository;
            _stdOperStepRepository = stdOperStepRepository;
            _itemProcessLinkRepository = itemProcessLinkRepository;
            _materialBOMRepository = materialBOMRepository;
            _materialBOMLinkRepository = materialBOMLinkRepository;
            _materialLinkRepository = materialLinkRepository;
            _materialRepository = materialRepository;
            _bomItemOperLinkRepository = bomItemOperLinkRepository;
            _bomItemOperStepLinkRepository = bomItemOperStepLinkRepository;
        }

        public async Task AddOperationAsync(OperInput operInput)
        {
            var processRoute = _processRouteRepository.Find(x => x.Id == operInput.Process_Route_Id);
            if (processRoute == null)
            {
                throw new CustomException(Messages.ProcessRouteNotFound);
            }

            string firstStdOperId = "";
            var stdOperRoutes = _stdOperEntRouteRepository.Query().Where(x => x.Process_Route_Id == operInput.Process_Route_Id).OrderBy(x => x.Order).ToList();
            if (stdOperRoutes == null || stdOperRoutes.Count == 0)
            {
                throw new CustomException(Messages.InputProcessRouteIsEmpty);
            }
            else if (stdOperRoutes.Count == 1)
            {
                if (stdOperRoutes.First().Oper_Id == stdOperRoutes.First().Input_Oper_Id)
                {
                    firstStdOperId = stdOperRoutes.First().Oper_Id;
                }
                else
                {
                    throw new CustomException(Messages.RouteNotFoundInProcessRoute);
                }
            }
            else
            {
                var inputStdOperIds = stdOperRoutes.Select(x => x.Input_Oper_Id).ToList();
                var stdOperIds = stdOperRoutes.Select(x => x.Oper_Id).ToList();
                if (inputStdOperIds.Count == 0 || stdOperIds.Count == 0)
                {
                    throw new CustomException(Messages.RouteNotFoundInProcessRoute);
                }
                foreach (var stdOperId in inputStdOperIds)
                {
                    if (!stdOperIds.Contains(stdOperId))
                    {
                        firstStdOperId = stdOperId;
                        break;
                    }
                }
            }

            var opers = new HashSet<Oper>();
            var firstStdOper = _stdOperRepository.Query().Where(x => x.Oper_Id == firstStdOperId).ToList();
            if (firstStdOper.Count == 0)
            {
                throw new CustomException(Messages.FirstStdOperInProcessRouteNotFound);
            }
            else if (firstStdOper.Count > 1)
            {
                throw new CustomException(Messages.FirstStdOperNotOnlyOne);
            }
            else
            {
                var oper = new Oper(operInput.Process_Id, firstStdOper.First().Oper_Id, firstStdOper.First().Oper_Desc, true, false, 1, firstStdOper.First().Oper_Cost,
                    firstStdOper.First().Oper_Type, firstStdOper.First().Deviation_Above, firstStdOper.First().Deviation_Below, firstStdOper.First().Notes, DateTime.Now);
                if (oper == null)
                {
                    throw new CustomException(Messages.ErrorCreatingOper);
                }
                if (stdOperRoutes.Count == 1)
                {
                    oper.Final_Oper = true;
                }
                opers.Add(oper);
            }

            var operEntLinks = new HashSet<OperEntLink>();
            var firstStdOperEntLinks = _stdOperEntLinkRepository.Query().Where(x => x.Oper_Id == firstStdOperId).ToList();
            if (firstStdOperEntLinks.Count == 0)
            {
                throw new CustomException(Messages.StdOperNotBindingEntity);
            }
            else
            {
                foreach (var item in firstStdOperEntLinks)
                {
                    if (!_productionUnitRepository.Exists(x => x.Id == item.Ent_Id))
                    {
                        throw new CustomException(Messages.EntityNotFound);
                    }
                    var operEntLink = new OperEntLink(operInput.Process_Id, item.Oper_Id, item.Ent_Id, item.Est_Transfer_Time, item.Est_Lab_Rate, item.Est_Setup_Time,
                        item.Est_Teardown_Time, item.Est_Prod_Rate, DateTime.Now);
                    if (operEntLink == null)
                    {
                        throw new CustomException(Messages.OperEntAdditionFailure);
                    }
                    operEntLinks.Add(operEntLink);
                }
            }

            var operAttrs = new HashSet<OperAttr>();
            var firstStdOperAttrs = _stdOperAttrRepository.Query().Where(x => x.Oper_Id == firstStdOperId).ToList();
            if (firstStdOperAttrs.Count > 0)
            {
                foreach (var stdOperAttr in firstStdOperAttrs)
                {
                    var operAttr = new OperAttr(operInput.Process_Id, stdOperAttr.Oper_Id, stdOperAttr.Attr_Id, stdOperAttr.Attr_Value, stdOperAttr.Notes, DateTime.Now);
                    if (operAttr == null)
                    {
                        throw new CustomException(Messages.OperAttrAdditionFailure);
                    }
                    operAttrs.Add(operAttr);
                }
            }

            var operStepGrps = new HashSet<OperStepGrp>();
            var firstStdOperStepGrps = _stdOperStepGrpRepository.Query().Where(x => x.Oper_Id == firstStdOperId).ToList();
            if (firstStdOperStepGrps.Count > 0)
            {
                foreach (var item in firstStdOperStepGrps)
                {
                    var operStepGrp = new OperStepGrp(operInput.Process_Id, item.Oper_Id, item.Step_Grp_Id, item.Step_Grp_Desc, item.Step_Grp_Seq, item.Last_Edit_Comment, DateTime.Now);
                    if (operStepGrp == null)
                    {
                        throw new CustomException(Messages.OperStepGroupAdditionFailure);
                    }
                    operStepGrps.Add(operStepGrp);
                }
            }

            var operSteps = new HashSet<OperStep>();
            var firstStdOperSteps = _stdOperStepRepository.Query().Where(x => x.Oper_Id == firstStdOperId).ToList();
            if (firstStdOperSteps.Count > 0)
            {
                foreach (var item in firstStdOperSteps)
                {
                    var operStep = new OperStep(operInput.Process_Id, item.Oper_Id, item.Step_Grp_Id, item.Step_No, item.Step_Seq, item.Step_Name,
                        item.Step_Desc, item.Last_Edit_Comment, DateTime.Now);
                    if (operStep == null)
                    {
                        throw new CustomException(Messages.OperStepAdditionFailure);
                    }
                    operSteps.Add(operStep);
                }
            }

            if (stdOperRoutes.Count > 1)
            {
                var operEntRoutes = new HashSet<OperEntRoute>();
                foreach (var route in stdOperRoutes)
                {
                    var stdOper = _stdOperRepository.Query().Where(x => x.Oper_Id == route.Oper_Id).ToList();
                    if (stdOper.Count == 0)
                    {
                        throw new CustomException(Messages.StdOperNotFound);
                    }
                    else if (stdOper.Count > 1)
                    {
                        throw new CustomException(Messages.StdOperNotOnlyOne);
                    }
                    else
                    {
                        var oper = new Oper(operInput.Process_Id, stdOper.First().Oper_Id, stdOper.First().Oper_Desc, false, false, route.Order + 1, stdOper.First().Oper_Cost,
                    stdOper.First().Oper_Type, stdOper.First().Deviation_Above, stdOper.First().Deviation_Below, stdOper.First().Notes, DateTime.Now);
                        if (oper == null)
                        {
                            throw new CustomException(Messages.ErrorCreatingOper);
                        }
                        if (route.Order == stdOperRoutes.Count)
                        {
                            oper.Final_Oper = true;
                        }
                        opers.Add(oper);
                    }

                    var operEntRoute = new OperEntRoute(operInput.Process_Id, route.Oper_Id, route.Ent_Id, route.Input_Oper_Id, route.Input_Ent_Id, route.Input_Percent,
                        route.Last_Edit_Comment, DateTime.Now);
                    if (operEntRoute == null)
                    {
                        throw new CustomException(Messages.ErrorCreatingOperEntRoute);
                    }
                    operEntRoutes.Add(operEntRoute);

                    var stdOperEntLinks = _stdOperEntLinkRepository.Query().Where(x => x.Oper_Id == route.Oper_Id).ToList();
                    if (stdOperEntLinks.Count == 0)
                    {
                        throw new CustomException(Messages.StdOperNotBindingEntity);
                    }
                    else
                    {
                        foreach (var item in stdOperEntLinks)
                        {
                            if (!_productionUnitRepository.Exists(x => x.Id == item.Ent_Id))
                            {
                                throw new CustomException(Messages.EntityNotFound);
                            }
                            var operEntLink = new OperEntLink(operInput.Process_Id, item.Oper_Id, item.Ent_Id, item.Est_Transfer_Time, item.Est_Lab_Rate, item.Est_Setup_Time,
                                item.Est_Teardown_Time, item.Est_Prod_Rate, DateTime.Now);
                            if (operEntLink == null)
                            {
                                throw new CustomException(Messages.OperEntAdditionFailure);
                            }
                            operEntLinks.Add(operEntLink);
                        }
                    }

                    var stdOperAttrs = _stdOperAttrRepository.Query().Where(x => x.Oper_Id == route.Oper_Id).ToList();
                    if (stdOperAttrs.Count > 0)
                    {
                        foreach (var stdOperAttr in stdOperAttrs)
                        {
                            var operAttr = new OperAttr(operInput.Process_Id, stdOperAttr.Oper_Id, stdOperAttr.Attr_Id, stdOperAttr.Attr_Value, stdOperAttr.Notes, DateTime.Now);
                            if (operAttr == null)
                            {
                                throw new CustomException(Messages.OperAttrAdditionFailure);
                            }
                            operAttrs.Add(operAttr);
                        }
                    }

                    var stdOperStepGrps = _stdOperStepGrpRepository.Query().Where(x => x.Oper_Id == route.Oper_Id).ToList();
                    if (stdOperStepGrps.Count > 0)
                    {
                        foreach (var item in stdOperStepGrps)
                        {
                            var operStepGrp = new OperStepGrp(operInput.Process_Id, item.Oper_Id, item.Step_Grp_Id, item.Step_Grp_Desc, item.Step_Grp_Seq, item.Last_Edit_Comment, DateTime.Now);
                            if (operStepGrp == null)
                            {
                                throw new CustomException(Messages.OperStepGroupAdditionFailure);
                            }
                            operStepGrps.Add(operStepGrp);
                        }
                    }

                    var stdOperSteps = _stdOperStepRepository.Query().Where(x => x.Oper_Id == route.Oper_Id).ToList();
                    if (stdOperSteps.Count > 0)
                    {
                        foreach (var item in stdOperSteps)
                        {
                            var operStep = new OperStep(operInput.Process_Id, item.Oper_Id, item.Step_Grp_Id, item.Step_No, item.Step_Seq, item.Step_Name,
                        item.Step_Desc, item.Last_Edit_Comment, DateTime.Now);
                            if (operStep == null)
                            {
                                throw new CustomException(Messages.OperStepAdditionFailure);
                            }
                            operSteps.Add(operStep);
                        }
                    }
                }
                if (operEntRoutes.Count > 0)
                {
                    await _operEntRouteRepository.AddRangeAsync(operEntRoutes);
                }
            }
            if (opers.Count > 0)
            {
                await _operRepository.AddRangeAsync(opers);
            }
            if (operEntLinks.Count > 0)
            {
                await _operEntLinkRepository.AddRangeAsync(operEntLinks);
            }
            if (operAttrs.Count > 0)
            {
                await _operAttrRepository.AddRangeAsync(operAttrs);
            }
            if (operStepGrps.Count > 0)
            {
                await _operStepGrpRepository.AddRangeAsync(operStepGrps);
            }
            if (operSteps.Count > 0)
            {
                await _operStepRepository.AddRangeAsync(operSteps);
            }
            int status = await _operRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.ErrorSavingOper);
            }
        }

        public async Task UpdateOperationAsync(OperUpdateInput operUpdateInput)
        {
            if (_processRepository.Find(x => x.Process_Id == operUpdateInput.Process_Id) == null)
            {
                throw new CustomException(Messages.ProcessNotFound);
            }

            var oper = _operRepository.Find(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id);
            if (oper == null)
            {
                throw new CustomException(Messages.OperNotFound);
            }

            if (operUpdateInput.OperEntLinks.Count == 0)
            {
                throw new CustomException(Messages.EntityNotFound);
            }

            bool needUpdateOperEntRoute = await UpdateOperEntitiesAsync(operUpdateInput);
            await UpdateOperAttributesAsync(operUpdateInput);
            await UpdateOperBOMsAsync(operUpdateInput);
            await UpdateOperStepGroupsAsync(operUpdateInput);
            await UpdateOperEntRoutesAsync(operUpdateInput);

            int status = 0;
            if (needUpdateOperEntRoute)
            {
                status = _operEntRouteRepository.Save();
                if (status <= 0)
                {
                    throw new CustomException(Messages.SavingFailure);
                }

                await UpdateOperEntRoutesAsync(operUpdateInput);
            }

            oper.Oper_Desc = operUpdateInput?.Oper_Desc;
            oper.Oper_Type = operUpdateInput?.Oper_Type;
            oper.Oper_Cost = operUpdateInput?.Oper_Cost;
            oper.Notes = operUpdateInput?.Notes;
            oper.Last_Edit_At = DateTime.Now;
            _operRepository.Update(oper);
            status = await _operRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.ErrorSavingOper);
            }
        }

        public async Task DeleteOperationAsync(string process_id, string[] oper_ids)
        {
            if (string.IsNullOrEmpty(process_id) || oper_ids.Length == 0)
            {
                throw new CustomException(Messages.InputArgsIlleagl);
            }

            if (_processRepository.Find(x => x.Process_Id == process_id) == null)
            {
                throw new CustomException(Messages.ProcessNotFound);
            }

            foreach (var operId in oper_ids)
            {
                var oper = _operRepository.Find(x => x.Process_Id == process_id && x.Oper_Id == operId);
                if (oper == null)
                {
                    throw new CustomException(Messages.OperNotFound);
                }
            }

            var operCounts = _operRepository.Query().Where(x => x.Process_Id == process_id).OrderBy(x => x.Display_Seq).ToList();
            if (operCounts.Count == 0)
            {
                throw new CustomException(Messages.OperNotFound);
            }

            _operEntLinkRepository.Delete(x => x.Process_Id == process_id && oper_ids.Contains(x.Oper_Id));
            _operAttrRepository.Delete(x => x.Process_Id == process_id && oper_ids.Contains(x.Oper_Id));
            _operStepGrpRepository.Delete(x => x.Process_Id == process_id && oper_ids.Contains(x.Oper_Id));
            _operStepRepository.Delete(x => x.Process_Id == process_id && oper_ids.Contains(x.Oper_Id));
            _bomItemOperLinkRepository.Delete(x => x.Process_Id == process_id && oper_ids.Contains(x.Oper_Id));
            _bomItemOperStepLinkRepository.Delete(x => x.Process_Id == process_id && oper_ids.Contains(x.Oper_Id));

            bool isLastOper = false;
            foreach (var operId in oper_ids)
            {
                var oper = _operRepository.Find(x => x.Process_Id == process_id && x.Oper_Id == operId);
                if (oper.Final_Oper)
                {
                    int sequence = oper.Display_Seq - 1;
                    var previous_oper = _operRepository.Find(x => x.Process_Id == process_id && x.Display_Seq == sequence);
                    if (previous_oper == null) // 当前工艺下仅有一道工序
                    {
                        isLastOper = true;
                        break;
                    }
                    previous_oper.Final_Oper = true;
                    _operRepository.Update(previous_oper);
                }
                else if (oper.First_Oper)
                {
                    var next_oper = _operRepository.Find(x => x.Process_Id == process_id && x.Display_Seq == oper.Display_Seq + 1);
                    if (next_oper == null) // 当前工艺下仅有一道工序
                    {
                        isLastOper = true;
                        break;
                    }
                    next_oper.First_Oper = true;
                    foreach (var op in operCounts)
                    {
                        if (op.Oper_Id != oper.Oper_Id)
                        {
                            op.Display_Seq -= 1;
                            _operRepository.Update(op);
                        }
                    }
                }
                else
                {
                    var operRoute = _operEntRouteRepository.Find(x => x.Process_Id == process_id && x.Oper_Id == oper.Oper_Id);
                    if (operRoute == null)
                    {
                        throw new CustomException(Messages.OperRouteNotFound);
                    }
                    var nextOperRoute = _operEntRouteRepository.Find(x => x.Process_Id == process_id && x.Input_Oper_Id == oper.Oper_Id);
                    if (nextOperRoute == null)
                    {
                        throw new CustomException(Messages.OperRouteNotFound);
                    }

                    var newNextOperRoute = new OperEntRoute(nextOperRoute.Process_Id, nextOperRoute.Oper_Id, nextOperRoute.Ent_Id, operRoute.Input_Oper_Id, operRoute.Input_Ent_Id,
                        operRoute.Input_Percent, operRoute.Last_Edit_Comment, DateTime.Now);
                    if (newNextOperRoute == null)
                    {
                        throw new CustomException(Messages.OperRouteCreatingFailure);
                    }
                    _operEntRouteRepository.Add(newNextOperRoute);
                    _operEntRouteRepository.Delete(nextOperRoute);

                    foreach (var op in operCounts)
                    {
                        if (op.Display_Seq > oper.Display_Seq && op.Display_Seq <= operCounts.Count)
                        {
                            op.Display_Seq -= 1;
                            _operRepository.Update(op);
                        }
                    }
                }
                _operEntRouteRepository.Delete(x => x.Process_Id == process_id && x.Oper_Id == oper.Oper_Id);
                _operRepository.Delete(x => x.Process_Id == process_id && x.Oper_Id == oper.Oper_Id);
                int status = _operRepository.Save();
                if (status <= 0)
                {
                    throw new CustomException(Messages.SavingFailure);
                }
                operCounts = _operRepository.Query().Where(x => x.Process_Id == process_id).OrderBy(x => x.Display_Seq).ToList();
            }

            if (isLastOper)
            {
                _operEntRouteRepository.Delete(x => x.Process_Id == process_id);
                _operRepository.Delete(x => x.Process_Id == process_id);
                int status = _operRepository.Save();
                if (status <= 0)
                {
                    throw new CustomException(Messages.SavingFailure);
                }
            }
        }

        public async Task<OperResult> GetOperationAsync(string process_id, string oper_id)
        {
            if (_processRepository.Find(x => x.Process_Id == process_id) == null)
            {
                throw new CustomException(Messages.ProcessNotFound);
            }

            var oper = _operRepository.Find(x => x.Process_Id == process_id && x.Oper_Id == oper_id);
            if (oper == null)
            {
                throw new CustomException(Messages.OperNotFound);
            }
            var data = _mapper.Map<OperResult>(oper);

            var operEntLinks = await _operEntLinkRepository.Query().Where(x => x.Process_Id == process_id && x.Oper_Id == oper_id).ToListAsync();
            if (operEntLinks.Count > 0)
            {
                var operEntLink = operEntLinks.Where(x => x.Oper_Id == oper_id).ToList();
                data.OperEntLinks = _mapper.Map<List<OperEntLinkResult>>(operEntLink);
                foreach (var element in data.OperEntLinks)
                {
                    var productionUnit = _productionUnitRepository.Find(x => x.Id == element.Ent_Id);
                    element.ProductionUnitCode = productionUnit.ProductionUnitCode;
                    element.ProductionUnitName = productionUnit.ProductionUnitName;
                }
            }

            var operAttrs = await _operAttrRepository.Query().Where(x => x.Process_Id == process_id && x.Oper_Id == oper_id).ToListAsync();
            if (operAttrs.Count > 0)
            {
                var operAttr = operAttrs.Where(x => x.Oper_Id == oper_id).ToList();
                data.OperAttrs = _mapper.Map<List<OperAttrResult>>(operAttr);
                foreach (var element in data.OperAttrs)
                {
                    var attr = _attrRepository.Find(x => x.Attr_Id == element.Attr_Id);
                    element.Attr_Desc = attr.Attr_Desc;
                    element.Data_Type = attr.Data_Type;
                }
            }

            var bomItems = await _bomItemOperLinkRepository.Query().Where(x => x.Process_Id == process_id && oper_id == x.Oper_Id).OrderBy(x => x.Mod_Id).ToListAsync();
            if (bomItems.Count > 0)
            {
                var bomItemsOperList = new List<BomItemOperLinkResult>();
                if (bomItemsOperList == null)
                {
                    throw new CustomException(Messages.ErrorCreatingOperBOM);
                }

                foreach (var bomItem in bomItems)
                {
                    var bom = _materialBOMRepository.Find(x => x.Id == bomItem.Ver_Id);
                    if (bom == null)
                    {
                        throw new CustomException(Messages.MaterialBOMNotFound);
                    }

                    var bomItemOperLinkResult = bomItemsOperList?.Find(x => x.Bom_Id == bomItem.Ver_Id);
                    if (bomItemOperLinkResult == null)
                    {
                        bomItemOperLinkResult = new BomItemOperLinkResult();
                        if (bomItemOperLinkResult == null)
                        {
                            throw new CustomException(Messages.ErrorCreatingOperBOM);
                        }

                        bomItemOperLinkResult.BomItems = new List<BomItemResult>();
                        if (bomItemOperLinkResult.BomItems == null)
                        {
                            throw new CustomException(Messages.OperBOMNotFound);
                        }
                        bomItemOperLinkResult.Bom_Id = bomItem.Ver_Id;
                        bomItemOperLinkResult.BomCode = bom.Code;
                        bomItemOperLinkResult.BomName = bom.Name;

                        bomItemsOperList?.Add(bomItemOperLinkResult);
                    }

                    var bomItemOper = new BomItemResult();
                    if (bomItemOper == null)
                    {
                        throw new CustomException(Messages.ErrorCreatingOperBOM);
                    }
                    bomItemOper.Bom_Pos = bomItem.Bom_Pos;
                    bomItemOper.Qty_Per_Parent_Item = bomItem.Qty_Per_Parent_Item;
                    var material = _materialRepository.Find(x => x.Id == bomItem.Bom_Pos);
                    if (material == null)
                    {
                        throw new CustomException(Messages.MaterialNotFound);
                    }
                    bomItemOper.MaterialCode = material.Code;
                    bomItemOper.MaterialName = material.Name;
                    if (bomItem.Bom_Pos == Convert.ToInt32(bomItem.Parent_Item_Id))
                    {
                        bomItemOper.Quantity = 1;
                        bomItemOper.RemainQuantiry = bomItemOper.Quantity - bomItemOper.Qty_Per_Parent_Item;
                    }
                    else
                    {
                        var materialBomMaterialLink = _materialLinkRepository.Find(x => x.MaterialId == bomItem.Bom_Pos && x.MaterialBOMId == bomItemOperLinkResult.Bom_Id);
                        if (materialBomMaterialLink == null)
                        {
                            throw new CustomException(Messages.MaterialBOMMaterialNotFound);
                        }
                        bomItemOper.Quantity = materialBomMaterialLink.MaterialNumber;
                        bomItemOper.RemainQuantiry = bomItemOper.Quantity - bomItemOper.Qty_Per_Parent_Item;
                    }
                    bomItemOperLinkResult.BomItems.Add(bomItemOper);
                }
                data.BomItemsOper = bomItemsOperList;
            }

            var operStepGrps = await _operStepGrpRepository.Query().Where(x => x.Process_Id == process_id && oper_id == x.Oper_Id).ToListAsync();
            if (operStepGrps.Count > 0)
            {
                var totalConsumeQuantities = new Dictionary<int, Dictionary<int, double>>();
                var operStepGrp = operStepGrps.Where(x => x.Oper_Id == oper_id).ToList();
                data.OperStepGrps = _mapper.Map<List<OperStepGrpResult>>(operStepGrp);
                foreach (var element in data.OperStepGrps)
                {
                    var operStep = _operStepRepository.Query().Where(x => x.Process_Id == process_id && x.Oper_Id == oper_id && x.Step_Grp_Id == element.Step_Grp_Id).ToList();
                    element.OperSteps = _mapper.Map<List<OperStepResult>>(operStep);
                    foreach(var step in element.OperSteps)
                    {
                        if (step != null)
                        {
                            var bomItemsOperStep = _bomItemOperStepLinkRepository.Query().Where(x => x.Process_Id == process_id && x.Oper_Id == oper_id && x.Step_Grp_Id == element.Step_Grp_Id && x.Step_No == step.Step_No).OrderBy(x => x.Mod_Id).ToListAsync();
                            if (bomItemsOperStep != null && bomItemsOperStep.Result.Count() > 0 && bomItems.Count > 0)
                            {
                                var bomItemsOperStepList = new List<BomItemOperLinkResult>();
                                if (bomItemsOperStepList == null)
                                {
                                    throw new CustomException(Messages.ErrorCreatingOperBOM);
                                }

                                foreach (var bomItem in bomItemsOperStep.Result)
                                {
                                    if (totalConsumeQuantities.ContainsKey(bomItem.Ver_Id))
                                    {
                                        if (totalConsumeQuantities[bomItem.Ver_Id].ContainsKey(bomItem.Bom_Pos))
                                        {
                                            totalConsumeQuantities[bomItem.Ver_Id][bomItem.Bom_Pos] += bomItem.Qty_Per_Parent_Item;
                                        }
                                        else
                                        {
                                            totalConsumeQuantities[bomItem.Ver_Id][bomItem.Bom_Pos] = bomItem.Qty_Per_Parent_Item;
                                        }
                                    }
                                    else
                                    {
                                        var materialQty = new Dictionary<int, double>();
                                        materialQty[bomItem.Bom_Pos] = bomItem.Qty_Per_Parent_Item;
                                        totalConsumeQuantities[bomItem.Ver_Id] = materialQty;
                                    }

                                    var bomItemOperStepLinkResult = bomItemsOperStepList.Find(x => x.Bom_Id == bomItem.Ver_Id);
                                    if (bomItemOperStepLinkResult == null)
                                    {
                                        bomItemOperStepLinkResult = new BomItemOperLinkResult();
                                        if (bomItemOperStepLinkResult == null)
                                        {
                                            throw new CustomException(Messages.ErrorCreatingOperStepBOM);
                                        }

                                        var bom = _materialBOMRepository.Find(x => x.Id == bomItem.Ver_Id);
                                        if (bom == null)
                                        {
                                            throw new CustomException(Messages.MaterialBOMNotFound);
                                        }
                                        bomItemOperStepLinkResult.BomItems = new List<BomItemResult>();
                                        if (bomItemOperStepLinkResult.BomItems == null)
                                        {
                                            throw new CustomException(Messages.MaterialBOMNotFound);
                                        }
                                        bomItemOperStepLinkResult.Bom_Id = bomItem.Ver_Id;
                                        bomItemOperStepLinkResult.BomCode = bom.Code;
                                        bomItemOperStepLinkResult.BomName = bom.Name;

                                        bomItemsOperStepList?.Add(bomItemOperStepLinkResult);
                                    }                                    

                                    var bomItemOperStep = new BomItemResult();
                                    if (bomItemOperStep == null)
                                    {
                                        throw new CustomException(Messages.OperStepBOMNotFound);
                                    }
                                    bomItemOperStep.Bom_Pos = bomItem.Bom_Pos;
                                    bomItemOperStep.Qty_Per_Parent_Item = bomItem.Qty_Per_Parent_Item;
                                    var material = _materialRepository.Find(x => x.Id == bomItem.Bom_Pos);
                                    if (material == null)
                                    {
                                        throw new CustomException(Messages.MaterialNotFound);
                                    }
                                    bomItemOperStep.MaterialCode = material.Code;
                                    bomItemOperStep.MaterialCode = material.Name;
                                    var materialBomMaterialLink = _bomItemOperLinkRepository.Find(x => x.Process_Id == process_id && x.Oper_Id == oper_id &&
                                    x.Parent_Item_Id == bomItem.Parent_Item_Id && x.Ver_Id == bomItem.Ver_Id && x.Bom_Pos == bomItem.Bom_Pos);
                                    if (materialBomMaterialLink == null)
                                    {
                                        throw new CustomException(Messages.MaterialNotFound);
                                    }
                                    bomItemOperStep.Quantity = materialBomMaterialLink.Qty_Per_Parent_Item;
                                    bomItemOperStepLinkResult.BomItems.Add(bomItemOperStep);
                                }

                                step.BomItemsOperStep = bomItemsOperStepList;
                            }
                        }
                    }

                    if (element.OperSteps == null)
                        goto L1;
                    foreach (var step in element.OperSteps)
                    {
                        if (step.BomItemsOperStep == null)
                            goto L1;
                        foreach (var bom in step.BomItemsOperStep)
                        {
                            if(bom.BomItems == null)
                                goto L1;
                            foreach (var item in bom.BomItems)
                            {
                                item.RemainQuantiry = item.Quantity - totalConsumeQuantities[bom.Bom_Id][item.Bom_Pos];
                                if (item.RemainQuantiry < 0)
                                {
                                    throw new CustomException(Messages.InvalidInputQuantity);
                                }
                            }
                        }
                    }

                L1:
                    continue;
                }
            }

            return data;
        }

        private async Task<bool> UpdateOperEntitiesAsync(OperUpdateInput operUpdateInput)
        {
            bool needUpdateOperEntRoute = false;
            var oldOperEntLinks = _operEntLinkRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id).ToListAsync();
            if (oldOperEntLinks.Result.Count > 0)
            {
                foreach (var item in oldOperEntLinks.Result)
                {
                    if (operUpdateInput.OperEntLinks.Count > 0)
                    {
                        var result = operUpdateInput.OperEntLinks.Where(x => x.Ent_Id == item.Ent_Id).ToList();
                        if (result.Count > 1)
                        {
                            throw new CustomException(Messages.EntityDuplication);
                        }
                        else if (result.Count == 0)
                        {
                            await _operEntLinkRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id && x.Ent_Id == item.Ent_Id);
                        }
                    }
                    else
                    {
                        await _operEntLinkRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id);
                        break;
                    }
                }
                needUpdateOperEntRoute = true;
            }

            Dictionary<int, int> entIds = new Dictionary<int, int>();
            var operEntLinks = new HashSet<OperEntLink>();
            foreach (var item in operUpdateInput.OperEntLinks)
            {
                if (!_productionUnitRepository.Exists(x => x.Id == item.Ent_Id))
                {
                    throw new CustomException(Messages.EntityNotFound);
                }

                if (entIds.ContainsKey(item.Ent_Id))
                {
                    throw new CustomException(Messages.EntityDuplication);
                }
                else
                {
                    entIds[item.Ent_Id] = 1;
                }

                var operEntLink = _operEntLinkRepository.Find(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Ent_Id == item.Ent_Id);
                if (operEntLink == null)
                {
                    operEntLink = _mapper.Map<OperEntLink>(item);
                    operEntLink.Process_Id = operUpdateInput.Process_Id;
                    operEntLink.Oper_Id = operUpdateInput.Oper_Id;
                    operEntLink.Last_Edit_At = DateTime.Now;
                    operEntLinks.Add(operEntLink);
                }
                else
                {
                    operEntLink.Est_Prod_Rate = item.Est_Prod_Rate;
                    operEntLink.Est_Lab_Rate = item?.Est_Lab_Rate;
                    operEntLink.Est_Setup_Time = item?.Est_Setup_Time;
                    operEntLink.Est_Teardown_Time = item?.Est_Teardown_Time;
                    operEntLink.Est_Transfer_Time = item?.Est_Transfer_Time;
                    operEntLink.Last_Edit_At = DateTime.Now;
                    _operEntLinkRepository.Update(operEntLink);
                }
            }
            if (operEntLinks.Count > 0)
            {
                needUpdateOperEntRoute = true;
                await _operEntLinkRepository.AddRangeAsync(operEntLinks);
            }
            return needUpdateOperEntRoute;
        }

        private async Task UpdateOperAttributesAsync(OperUpdateInput operUpdateInput)
        {
            var oldOperAttrs = _operAttrRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id).ToListAsync();
            if (oldOperAttrs.Result.Count > 0)
            {
                foreach (var item in oldOperAttrs.Result)
                {
                    if (operUpdateInput?.OperAttrs?.Count > 0)
                    {
                        var result = operUpdateInput.OperAttrs.Where(x => x.Attr_Id == item.Attr_Id).ToList();
                        if (result.Count > 1)
                        {
                            throw new CustomException(Messages.EntityDuplication);
                        }
                        else if (result.Count == 0)
                        {
                            await _operAttrRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id && x.Attr_Id == item.Attr_Id);
                        }
                    }
                    else
                    {
                        await _operAttrRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id);
                        break;
                    }
                }
            }

            if (operUpdateInput?.OperAttrs?.Count > 0)
            {
                Dictionary<int, int> attrIds = new Dictionary<int, int>();
                var operAttrs = new HashSet<OperAttr>();
                foreach (var item in operUpdateInput.OperAttrs)
                {
                    if (attrIds.ContainsKey(item.Attr_Id))
                    {
                        throw new CustomException(Messages.AttrDuplication);
                    }
                    else
                    {
                        attrIds[item.Attr_Id] = 1;
                    }

                    if (!_attrRepository.Exists(x => x.Attr_Id == item.Attr_Id))
                    {
                        throw new CustomException(Messages.AttrNotFound);
                    }

                    var operAttr = _operAttrRepository.Find(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Attr_Id == item.Attr_Id);
                    if (operAttr == null)
                    {
                        operAttr = _mapper.Map<OperAttr>(item);
                        operAttr.Process_Id = operUpdateInput.Process_Id;
                        operAttr.Oper_Id = operUpdateInput.Oper_Id;
                        operAttrs.Add(operAttr);
                    }
                    else
                    {
                        operAttr.Attr_Value = item.Attr_Value;
                        operAttr.Notes = item.Notes;
                        operAttr.Last_Edit_At = DateTime.Now;
                        _operAttrRepository.Update(operAttr);
                    }
                }
                if (operAttrs.Count > 0)
                {
                    await _operAttrRepository.AddRangeAsync(operAttrs);
                }
            }
        }

        private async Task UpdateOperBOMsAsync(OperUpdateInput operUpdateInput)
        {
            if (operUpdateInput?.BomItemsOper != null && operUpdateInput.BomItemsOper.BomItems.Count > 0)
            {
                var materials = _itemProcessLinkRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id).ToList();
                if (materials == null || materials.Count == 0)
                {
                    throw new CustomException(Messages.MaterialNotFound);
                }
                else if (materials.Count == 1)
                {
                    var boms = _materialBOMLinkRepository.Query().Where(x => x.MaterialId == materials.First().MaterialId).ToList();
                    if (boms == null || boms.Count == 0)
                    {
                        throw new CustomException(Messages.MaterialBOMNotFound);
                    }
                    var bomIds = boms.Select(x => x.MaterialBOMId).ToList();
                    int inputBomId = operUpdateInput.BomItemsOper.Bom_Id;
                    if (!bomIds.Contains(inputBomId))
                    {
                        throw new CustomException(Messages.MaterialBOMNotFound);
                    }
                    var bom = _materialBOMRepository.Find(x => x.Id == inputBomId);
                    if (bom == null)
                    {
                        throw new CustomException(Messages.MaterialBOMNotFound);
                    }

                    // 清空工序BOM表中待生产物料的选定BOM下所有的物料信息
                    await _bomItemOperLinkRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Ver_Id == inputBomId && x.Parent_Item_Id == materials.First().MaterialId.ToString());

                    Dictionary<int, int> materialIds = new Dictionary<int, int>();
                    var bomItemOperLinks = new HashSet<BomItemOperLink>();
                    int displaySeq = 0;
                    foreach (var item in operUpdateInput.BomItemsOper.BomItems)
                    {
                        if (materialIds.ContainsKey(item.Bom_Pos))
                        {
                            throw new CustomException(Messages.MaterialDuplication);
                        }
                        else
                        {
                            materialIds[item.Bom_Pos] = 1;
                        }

                        var materialLink = _materialLinkRepository.Find(x => x.MaterialId == item.Bom_Pos && x.MaterialBOMId == bom.Id);
                        if (materialLink == null && item.Bom_Pos != materials.First().MaterialId)
                        {
                            throw new CustomException(Messages.MaterialBOMMaterialNotFound);
                        }
                        double quantity = 0;
                        if (item.Bom_Pos == materials.First().MaterialId)
                        {
                            quantity = 1;
                        }
                        else
                        {
                            quantity = materialLink.MaterialNumber;
                        }
                        if (item.Qty_Per_Parent_Item > quantity)
                        {
                            throw new CustomException(Messages.InputQuantityIlleagl);
                        }

                        var bomItemOperLink = new BomItemOperLink(materials.First().MaterialId.ToString(), inputBomId, item.Bom_Pos, operUpdateInput.Process_Id,
                            operUpdateInput.Oper_Id, item.Qty_Per_Parent_Item, displaySeq, DateTime.Now);
                        if (bomItemOperLink == null)
                        {
                            throw new CustomException(Messages.ErrorCreatingOperBOM);
                        }
                        bomItemOperLinks.Add(bomItemOperLink);
                        displaySeq++;
                    }

                    if (bomItemOperLinks.Count > 0)
                    {
                        await _bomItemOperLinkRepository.AddRangeAsync(bomItemOperLinks);
                    }
                }
                else
                {
                    throw new CustomException(Messages.CurrentVersionNotSupport);
                }
            }
        }

        private async Task UpdateOperStepGroupsAsync(OperUpdateInput operUpdateInput)
        {
            var oldOperStepGrps = _operStepGrpRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id).ToListAsync();
            if (oldOperStepGrps.Result.Count > 0)
            {
                foreach (var item in oldOperStepGrps.Result)
                {
                    if (operUpdateInput?.OperStepGrps?.Count > 0)
                    {
                        var result = operUpdateInput.OperStepGrps.Where(x => x.Step_Grp_Id == item.Step_Grp_Id).ToList();
                        if (result.Count > 1)
                        {
                            throw new CustomException(Messages.StepGroupDuplication);
                        }
                        else if (result.Count == 0) // 若更新的工步组的id不在工步组表中，则将工步组表中的工步组删除，同时删除工步表中此工步组的工步
                        {
                            await _operStepGrpRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id);
                            await _operStepRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id);
                        }
                        else
                        {
                            var oldOperSteps = _operStepRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id).ToListAsync();
                            if (oldOperSteps.Result.Count > 0) // 更新前工步表中含有此工步组的工步
                            {
                                foreach (var element in result) // 更新的工步组的id可以在工步组表中找到
                                {
                                    if (element.OperSteps.Count > 0) // 更新的工步组中含工步
                                    {
                                        foreach (var e in oldOperSteps.Result)
                                        {
                                            var step = element.OperSteps.Where(x => x.Step_No == e.Step_No).ToList();
                                            if (step.Count == 0) // 删除表中存在但更新的工序输入中不存在的工步
                                            {
                                                await _operStepRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id && x.Step_No == e.Step_No);
                                            }
                                        }
                                    }
                                    else // 更新的工步组中不含工步，则需删除工步表中此工步组所含的工步
                                    {
                                        await _operStepRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id);
                                    }
                                }
                            }
                        }
                    }
                    else // 若更新中不含任何工步组，则删除此工序的所有工步组和工步
                    {
                        await _operStepGrpRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id);
                        await _operStepRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == item.Oper_Id);
                        break;
                    }
                }
            }

            if (operUpdateInput?.OperStepGrps?.Count > 0)
            {
                Dictionary<int, int> stepGrpIds = new Dictionary<int, int>();
                Dictionary<int, double> materialQuantity = new Dictionary<int, double>();
                var operStepGrps = new HashSet<OperStepGrp>();
                foreach (var item in operUpdateInput.OperStepGrps)
                {
                    if (stepGrpIds.ContainsKey(item.Step_Grp_Id))
                    {
                        throw new CustomException(Messages.StepGroupDuplication);
                    }
                    else
                    {
                        stepGrpIds[item.Step_Grp_Id] = 1;
                    }

                    Dictionary<int, int> stepIds = new Dictionary<int, int>();
                    var operSteps = new HashSet<OperStep>();
                    var operStepGrp = _operStepGrpRepository.Find(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id);
                    if (operStepGrp == null) // 若输入的工步组不存在则新增工步组和工步
                    {
                        operStepGrp = _mapper.Map<OperStepGrp>(item);
                        operStepGrp.Process_Id = operUpdateInput.Process_Id;
                        operStepGrp.Oper_Id = operUpdateInput.Oper_Id;
                        operStepGrp.Last_Edit_At = DateTime.Now;
                        operStepGrps.Add(operStepGrp);
                        foreach (var element in item.OperSteps)
                        {
                            if (stepIds.ContainsKey(element.Step_No))
                            {
                                throw new CustomException(Messages.StepDuplication);
                            }
                            else
                            {
                                stepIds[element.Step_No] = 1;
                            }

                            var operStep = _mapper.Map<OperStep>(element);
                            operStep.Process_Id = operUpdateInput.Process_Id;
                            operStep.Oper_Id = operUpdateInput.Oper_Id;
                            operStep.Step_Grp_Id = item.Step_Grp_Id;
                            operStep.Last_Edit_At = DateTime.Now;
                            operSteps.Add(operStep);

                            if (operUpdateInput.BomItemsOper != null && operUpdateInput.BomItemsOper.BomItems.Count > 0 && element.BomItemsOperStep != null && element.BomItemsOperStep.BomItems.Count > 0)
                            {
                                var materials = _itemProcessLinkRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id).ToList();
                                if (materials == null || materials.Count == 0)
                                {
                                    throw new CustomException(Messages.MaterialNotFound);
                                }
                                else if (materials.Count == 1)
                                {
                                    var boms = _materialBOMLinkRepository.Query().Where(x => x.MaterialId == materials.First().MaterialId).ToList();
                                    if (boms == null || boms.Count == 0)
                                    {
                                        throw new CustomException(Messages.MaterialBOMNotFound);
                                    }
                                    var bomIds = boms.Select(x => x.MaterialBOMId).ToList();
                                    int inputBomId = element.BomItemsOperStep.Bom_Id;
                                    if (!bomIds.Contains(inputBomId))
                                    {
                                        throw new CustomException(Messages.MaterialBOMNotFound);
                                    }
                                    var bom = _materialBOMRepository.Find(x => x.Id == inputBomId);
                                    if (bom == null)
                                    {
                                        throw new CustomException(Messages.MaterialBOMNotFound);
                                    }

                                    await _bomItemOperStepLinkRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Ver_Id == inputBomId
                                            && x.Parent_Item_Id == materials.First().MaterialId.ToString() && x.Step_Grp_Id == item.Step_Grp_Id && x.Step_No == element.Step_No);

                                    Dictionary<int, int> materialIds = new Dictionary<int, int>();
                                    var bomItemOperStepLinks = new HashSet<BomItemOperStepLink>();
                                    int displaySeq = 0;
                                    foreach (var material in element.BomItemsOperStep.BomItems)
                                    {
                                        if (materialIds.ContainsKey(material.Bom_Pos))
                                        {
                                            throw new CustomException(Messages.MaterialDuplication);
                                        }
                                        else
                                        {
                                            materialIds[material.Bom_Pos] = 1;
                                        }

                                        if (materialQuantity.ContainsKey(material.Bom_Pos))
                                        {
                                            materialQuantity[material.Bom_Pos] += material.Qty_Per_Parent_Item;
                                        }
                                        else
                                        {
                                            materialQuantity[material.Bom_Pos] = material.Qty_Per_Parent_Item;
                                        }

                                        // 所有工步组中所有工步下BOM中单一物料的数量和，不能超过工序下BOM中单一物料配置的数量
                                        foreach (var x in operUpdateInput.BomItemsOper.BomItems)
                                        {
                                            if (x.Bom_Pos == material.Bom_Pos && x.Qty_Per_Parent_Item < materialQuantity[material.Bom_Pos])
                                            {
                                                throw new CustomException(Messages.InputQuantityIlleagl);
                                            }
                                        }

                                        var bomItemOperStep = new BomItemOperStepLink(materials.First().MaterialId.ToString(), inputBomId, material.Bom_Pos, operUpdateInput.Process_Id, operUpdateInput.Oper_Id,
                                            item.Step_Grp_Id, element.Step_No, material.Qty_Per_Parent_Item, displaySeq, DateTime.Now);
                                        if (bomItemOperStep == null)
                                        {
                                            throw new CustomException(Messages.ErrorCreatingOperStepBOM);
                                        }
                                        bomItemOperStepLinks.Add(bomItemOperStep);
                                        displaySeq++;
                                    }
                                    if (bomItemOperStepLinks.Count > 0)
                                    {
                                        await _bomItemOperStepLinkRepository.AddRangeAsync(bomItemOperStepLinks);
                                    }
                                }
                                else
                                {
                                    throw new CustomException(Messages.CurrentVersionNotSupport);
                                }
                            }
                        }
                    }
                    else
                    {
                        operStepGrp.Step_Grp_Desc = item.Step_Grp_Desc; // 若输入的工步组存在则更新工步组
                        operStepGrp.Step_Grp_Seq = item.Step_Grp_Seq;
                        operStepGrp.Last_Edit_Comment = item?.Last_Edit_Comment;
                        operStepGrp.Last_Edit_At = DateTime.Now;
                        _operStepGrpRepository.Update(operStepGrp);
                        foreach (var element in item.OperSteps)
                        {
                            if (stepIds.ContainsKey(element.Step_No))
                            {
                                throw new CustomException(Messages.StepDuplication);
                            }
                            else
                            {
                                stepIds[element.Step_No] = 1;
                            }

                            var operStep = _operStepRepository.Find(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id && x.Step_No == element.Step_No);
                            if (operStep == null) // 若输入的工步组存在但其下的工步不存在，则新增工步
                            {
                                operStep = _mapper.Map<OperStep>(element);
                                operStep.Process_Id = operUpdateInput.Process_Id;
                                operStep.Oper_Id = operUpdateInput.Oper_Id;
                                operStep.Step_Grp_Id = item.Step_Grp_Id;
                                operStep.Last_Edit_At = DateTime.Now;
                                operSteps.Add(operStep);
                            }
                            else // 若输入的工步组存在且其下的工步也存在，则更新工步
                            {
                                operStep.Step_Seq = element.Step_Seq;
                                operStep.Step_Name = element?.Step_Name;
                                operStep.Step_Desc = element.Step_Desc;
                                operStep.Last_Edit_Comment = element?.Last_Edit_Comment;
                                operStep.Last_Edit_At = DateTime.Now;
                                _operStepRepository.Update(operStep);
                            }

                            if (operUpdateInput.BomItemsOper != null && operUpdateInput.BomItemsOper.BomItems.Count > 0 && element?.BomItemsOperStep != null && element.BomItemsOperStep.BomItems.Count > 0)
                            {
                                var materials = _itemProcessLinkRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id).ToList();
                                if (materials == null || materials.Count == 0)
                                {
                                    throw new CustomException(Messages.MaterialNotFound);
                                }
                                else if (materials.Count == 1)
                                {
                                    var boms = _materialBOMLinkRepository.Query().Where(x => x.MaterialId == materials.First().MaterialId).ToList();
                                    if (boms == null || boms.Count == 0)
                                    {
                                        throw new CustomException(Messages.MaterialBOMNotFound);
                                    }
                                    var bomIds = boms.Select(x => x.MaterialBOMId).ToList();
                                    int inputBomId = element.BomItemsOperStep.Bom_Id;
                                    if (!bomIds.Contains(inputBomId))
                                    {
                                        throw new CustomException(Messages.MaterialBOMNotFound);
                                    }
                                    var bom = _materialBOMRepository.Find(x => x.Id == inputBomId);
                                    if (bom == null)
                                    {
                                        throw new CustomException(Messages.MaterialBOMNotFound);
                                    }

                                    await _bomItemOperStepLinkRepository.DeleteAsync(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id && x.Ver_Id == inputBomId
                                            && x.Parent_Item_Id == materials.First().MaterialId.ToString() && x.Step_Grp_Id == item.Step_Grp_Id && x.Step_No == element.Step_No);

                                    Dictionary<int, int> materialIds = new Dictionary<int, int>();
                                    var bomItemOperStepLinks = new HashSet<BomItemOperStepLink>();
                                    int displaySeq = 0;
                                    foreach (var material in element.BomItemsOperStep.BomItems)
                                    {
                                        if (materialIds.ContainsKey(material.Bom_Pos))
                                        {
                                            throw new CustomException(Messages.MaterialDuplication);
                                        }
                                        else
                                        {
                                            materialIds[material.Bom_Pos] = 1;
                                        }

                                        if (materialQuantity.ContainsKey(material.Bom_Pos))
                                        {
                                            materialQuantity[material.Bom_Pos] += material.Qty_Per_Parent_Item;
                                        }
                                        else
                                        {
                                            materialQuantity[material.Bom_Pos] = material.Qty_Per_Parent_Item;
                                        }

                                        // 输入中工序的工步下BOM中物料的消耗量需不超过工序下BOM中物料配置的数量
                                        foreach (var x in operUpdateInput.BomItemsOper.BomItems)
                                        {
                                            if (x.Bom_Pos == material.Bom_Pos && x.Qty_Per_Parent_Item < materialQuantity[material.Bom_Pos])
                                            {
                                                throw new CustomException(Messages.InputQuantityIlleagl);
                                            }
                                        }

                                        var bomItemOperStep = new BomItemOperStepLink(materials.First().MaterialId.ToString(), inputBomId, material.Bom_Pos, operUpdateInput.Process_Id,
                                            operUpdateInput.Oper_Id, item.Step_Grp_Id, element.Step_No, material.Qty_Per_Parent_Item, displaySeq, DateTime.Now);
                                        if (bomItemOperStep == null)
                                        {
                                            throw new CustomException(Messages.ErrorCreatingOperStepBOM);
                                        }
                                        bomItemOperStepLinks.Add(bomItemOperStep);
                                        displaySeq++;
                                    }
                                    if (bomItemOperStepLinks.Count > 0)
                                    {
                                        await _bomItemOperStepLinkRepository.AddRangeAsync(bomItemOperStepLinks);
                                    }
                                }
                                else
                                {
                                    throw new CustomException(Messages.CurrentVersionNotSupport);
                                }
                            }
                        }
                    }
                    if (operSteps.Count > 0)
                    {
                        await _operStepRepository.AddRangeAsync(operSteps);
                    }
                }
                if (operStepGrps.Count > 0)
                {
                    await _operStepGrpRepository.AddRangeAsync(operStepGrps);
                }
            }
        }

        private async Task UpdateOperEntRoutesAsync(OperUpdateInput operUpdateInput)
        {
            var routes = _operEntRouteRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id).ToList();
            if (routes.Count() > 0)
            {
                var operEnts = _operEntLinkRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id).ToListAsync();
                if (operEnts == null || operEnts.Result.Count == 0)
                {
                    throw new CustomException(Messages.OperEntNotFound);
                }
                else if (operEnts.Result.Count == 1)
                {
                    var operEntLink = operEnts.Result.First();
                    var operRoute = _operEntRouteRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Oper_Id == operUpdateInput.Oper_Id);
                    if (operRoute == null || operRoute.Count() == 0)
                    {
                        operRoute = _operEntRouteRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Input_Oper_Id == operUpdateInput.Oper_Id);
                        if (operRoute == null || operRoute.Count() == 0)
                        {
                            throw new CustomException(Messages.OperRouteNotFound);
                        }
                        var newOperRoute = new OperEntRoute(operRoute.First().Process_Id, operRoute.First().Oper_Id, operRoute.First().Ent_Id, operRoute.First().Input_Oper_Id,
                            operEntLink.Ent_Id, operRoute.First().Input_Percent, operRoute.First().Last_Edit_Comment, DateTime.Now);
                        _operEntRouteRepository.Delete(operRoute.First());
                        _operEntRouteRepository.Add(newOperRoute);
                    }
                    else
                    {
                        var newOperRoute = new OperEntRoute(operRoute.First().Process_Id, operRoute.First().Oper_Id, operEntLink.Ent_Id, operRoute.First().Input_Oper_Id,
                            operRoute.First().Input_Ent_Id, operRoute.First().Input_Percent, operRoute.First().Last_Edit_Comment, DateTime.Now);
                        _operEntRouteRepository.Delete(operRoute.First());
                        _operEntRouteRepository.Add(newOperRoute);

                        var inputOperIds = routes.Select(x => x.Input_Oper_Id).ToList();
                        if (inputOperIds.Contains(operUpdateInput.Oper_Id))
                        {
                            var inputOperRoute = _operEntRouteRepository.Query().Where(x => x.Process_Id == operUpdateInput.Process_Id && x.Input_Oper_Id == operUpdateInput.Oper_Id);
                            var newInputOperRoute = new OperEntRoute(inputOperRoute.First().Process_Id, inputOperRoute.First().Oper_Id, inputOperRoute.First().Ent_Id,
                                inputOperRoute.First().Input_Oper_Id, operEntLink.Ent_Id, inputOperRoute.First().Input_Percent, inputOperRoute.First().Last_Edit_Comment,
                                DateTime.Now);
                            _operEntRouteRepository.Delete(inputOperRoute.First());
                            _operEntRouteRepository.Add(newInputOperRoute);
                        }
                    }
                }
                else // 一道工序多个实体，对应多条路线，当前不必支持此场景
                {
                    throw new CustomException(Messages.CurrentVersionNotSupport);
                }
            }
        }
    }
}
